﻿/*
 * Created by SharpDevelop.
 * User: phtrung
 * Date: 6/5/2009
 * Time: 10:07 AM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Net.Security;
using System.Text.RegularExpressions;
using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;

namespace popLib.ExtensionClass
{
	/// <summary>
	/// Description of Imap.
	/// </summary>
	public class Imap
	{
		private TcpClient _imapClient;
        private NetworkStream _imapNs;
        private StreamWriter _imapSw;
        private StreamReader _imapSr;
        private SslStream _imapNsSSL;
        private bool useSSL = false;
        private int CommandCounter = 0;
        
		private string currentMailBox = "INBOX";
                
        public Imap()
        {

        }

        public Imap(string hostname, int port, bool useSSL)
        {
            InitializeConnection(hostname, port, useSSL);
        }

        public bool Connect(string hostname, int port, bool useSSL)
        {
            return InitializeConnection(hostname, port, useSSL);
        }

        public void Disconnect()
        {
            _imapSw.WriteLine("$ LOGOUT");
            _imapSw.Flush();
            _imapClient.Close();
        }
        
        private bool InitializeConnection(string hostname, int port, bool useSSL)
        {
            try
            {
            	this.useSSL = useSSL;
                _imapClient = new TcpClient(hostname, port);                
                
                if(useSSL){                	
                	_imapNsSSL = new SslStream( _imapClient.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
                	_imapNsSSL.AuthenticateAsClient(hostname);
                	_imapSw = new StreamWriter(_imapNsSSL);
                	_imapSr = new StreamReader(_imapNsSSL);
                }else{
                	_imapNs = _imapClient.GetStream();
                	_imapSw = new StreamWriter(_imapNs);
                	_imapSr = new StreamReader(_imapNs);
                }
                
                string result = _imapSr.ReadLine();
                if ( result.StartsWith("* OK")== true ){
	                SendAndReceive( "CAPABILITY" );
	                return true;
                }
            }
            catch (SocketException ex)
            {
            	Console.WriteLine("[InitializeConnection]"+ex);               
            }
             return false;
        }

        private ReturnSet SendAndReceive(string command){
        	CommandCounter++;
        	string imapCommandIndex = "IMAP" + StringLib.spaceLeftInclude( CommandCounter, 3, "0" ) + " ";
        	
    		_imapSw.WriteLine( imapCommandIndex + command );
           	_imapSw.Flush();
           	
           	bool readNext = true;
           	ReturnSet result = new ReturnSet( false, null );
           	string receivedStr = "";
			while (readNext)
			{
                string lineStr = _imapSr.ReadLine();
				receivedStr += lineStr+"\r\n";
				if( lineStr.StartsWith( imapCommandIndex+"OK" ) ){ readNext = false; result.Success = true; }
				if( lineStr.StartsWith( imapCommandIndex+"NO" ) ) readNext = false;
				if( lineStr.StartsWith( imapCommandIndex+"BAD" ) ) readNext = false;
			}
			result.Result = receivedStr;
			return result;
        }
        
        private string Response(){return _imapSr.ReadToEnd();}

        public bool AuthenticateUser(string username, string password)
        {
        	try{
        		return SendAndReceive( string.Format( "LOGIN {0} {1}", username, password ) ).Success;
        	}catch(Exception ex){
        		Console.WriteLine("[AuthenticateUser]"+ex);
        		return false;
        	}
        }
		       
        public object GetMessageHeaders(int index)
        {
            _imapSw.WriteLine("$ FETCH " + index + " (body[header.fields (from subject date)])");
            _imapSw.Flush();

            return Response();
        }

        public object GetMessage(int index)
        {
            _imapSw.WriteLine("$ FETCH " + index + " body[text]");
            _imapSw.Flush();

            return Response();
        }


        
		
        public static bool ValidateServerCertificate( object sender, X509Certificate certificate,X509Chain chain,SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

            // Do not allow this client to communicate with unauthenticated servers.
            return false;
        }
		
        public string CurrentMailBox{
        	get{ return currentMailBox; }
        	set{ if(StringLib.isValid(value)) currentMailBox = value; }
        }
        
        public int TotalMail{ 
        	get{ 
        		ReturnSet rs = SendAndReceive( "SELECT "+currentMailBox );
        		if( rs.Success && rs.Result.ToString().IndexOf("EXISTS") > 0 ){
        			string[] lines = StringLib.SplitByAny( rs.Result.ToString(), "\r\n".Split(',') );
					foreach( string line in lines ){
						if( line.EndsWith("EXISTS") ) 
							return NumberLib.getInt( StringLib.subStringBetween(line, "* ", " EXISTS", 0) );
					}
        		}
        		return -1;
        	}
        }
        public int UnreadMail{ 
        	get{
        		ReturnSet rs = SendAndReceive( "STATUS "+currentMailBox+" (unseen)" );
        		if( rs.Success && rs.Result.ToString().IndexOf("UNSEEN") > 0 ){
        			return NumberLib.getInt( StringLib.subStringBetween( rs.Result.ToString(), "UNSEEN", ")", 0 ) );
        		}
        		return -1;
        	} 
        }
	}	
	
}
